//====================================
// brief: 程序的main入口文件，调用glut库来画经过三角剖分后的多边形网格和寻路的路径
// author:sunxvming@163.com
// date:  2019-11-16
//====================================
#include <cstdio>
#include <stdio.h>
#include <iostream>
#include <memory>
#include <GL/glut.h>
#include "Polygon.h"

int fromx = -1;
int fromy = -1;
int tox = -1;
int toy = -1;
int clicknum = 0;

// 两两为一组，定义了x个点坐标，每两个点之间有一条约束边，约束边互相穿过的话会导致程序无法正确进行DT三角剖分，陷入死循环
double pos[] = {50, 50, 70, 50, 120, 50, 275, 100, 425, 60, 450, 160, 350, 230, 600, 325, 750, 200, 750, 500, 525, 460, 575, 550, 425, 550, 480, 450, 325, 425, 300, 300, 30, 400, 85, 275};
// double pos[] = {0, 126, 119, 118, 158, 0, 200, 122, 317, 120, 224, 201, 260, 317, 163, 245, 65, 316, 99, 196};
// double pos[] = {150, 100, 300, 100, 350, 150, 400, 100, 550, 100, 600, 150, 450, 450, 200, 500, 200, 350, 50, 250};
// double pos[] = {50, 150,150,100,200,100,250,200, 300, 150,250,250,200,250,150,200,150,150,50,250};

navmesh::Polygon * polygon = new navmesh::Polygon(pos, sizeof(pos) / sizeof(double));
vector<Point> ways;

static void DrawMap()
{
	// （0,0）is in the upper left corner
	// 图形库里一般以屏幕的从上到下，作为y轴的正向

	glLineWidth(2); // 设置绘制的线段宽度
	glBegin(GL_LINES); // 开始绘制一批线段

	// draw grid lines
	vector<Line> gridelines = polygon->GetGrideLines();
	for (unsigned i = 0; i < gridelines.size(); i++)
	{
		Line grideline = gridelines[i];
		glColor3fv(grideline.color);
		glVertex2f((GLfloat)grideline.p1.x, (GLfloat)grideline.p1.y);
		glVertex2f((GLfloat)grideline.p2.x, (GLfloat)grideline.p2.y);
	}
	glEnd();

	// draw triangle that path has been found
	// 绘制寻路路径上经过的三角形
	for (const auto &tri : polygon->triangles)
	{
		if (tri.isPath && ways.size() > 0)
		{
			glBegin(GL_TRIANGLES); // 开始绘制1个三角形
			glColor3f(0.5f, 0.5f, 0.5f); // 设置三角形背景色为灰色
			glVertex2f((GLfloat)polygon->points[tri.p1].x, (GLfloat)polygon->points[tri.p1].y);
			glVertex2f((GLfloat)polygon->points[tri.p2].x, (GLfloat)polygon->points[tri.p2].y);
			glVertex2f((GLfloat)polygon->points[tri.p3].x, (GLfloat)polygon->points[tri.p3].y);
			glEnd();
		}
	}

	// draw polygon lines
	// 画多边形的内部边和内部的约束边，用不同的颜色区分
	glBegin(GL_LINES);
	vector<Line> lines = polygon->GetLines();
	for (unsigned i = 0; i < lines.size(); i++)
	{
		Line line = lines[i];
		glColor3fv(line.color);
		glVertex2f((GLfloat)line.p1.x, (GLfloat)line.p1.y);
		glVertex2f((GLfloat)line.p2.x, (GLfloat)line.p2.y);
	}

	// draw path lines
	// 画路径
	for (unsigned i = 1; i < ways.size(); i++)
	{
		glColor3f(0.0f, 0.0, 1.0);
		glVertex2f((GLfloat)ways[i - 1].x, (GLfloat)ways[i - 1].y);
		glVertex2f((GLfloat)ways[i].x, (GLfloat)ways[i].y);
	}
	glEnd();

	// draw triangle centers
	// 画三角形的重心
	glPointSize(3);
	glBegin(GL_POINTS);
	auto centers = polygon->GetCenters();
	for (unsigned i = 0; i < centers.size(); i++)
	{
		glColor3f(0.5f, 0.5, 0.5);
		glVertex3f(centers[i].x, centers[i].y, 0.0);
	}
	glEnd();

	// draw start and end point
	// 画起点和终点
	glPointSize(5);
	glBegin(GL_POINTS);
	glColor3f(1.0f, 0.0, 0.0);
	if (fromx != -1 && fromy != -1)
	{
		glVertex3f(fromx, fromy, 0.0);
	}
	if (tox != -1 && toy != -1)
	{
		glVertex3f(tox, toy, 0.0);
	}
	glEnd();
}

void myDisplay(void)
{
	// glClearColor(0.2f, 0.3f, 0.3f, 0.3f); // 设置背景颜色
	// 清除颜色缓冲区
	glClear(GL_COLOR_BUFFER_BIT);
	// 画图
	DrawMap();
	// 完成渲染，执行并清空命令队列
	glFlush();
}

// 鼠标点击的回调函数，目的为获取用户的鼠标点输入，并作为寻路的起点和终点来进行寻路
// param ：
//	button: GLUT_LEFT_BUTTON或GLUT_RIGHT_BUTTON分别表示左右按键。
//	state: 按键的状态，若为按下则为GLUT_DOWN
void myClick(int button, int state, int x, int y)
{
	if (state == GLUT_UP)
	{
		ways.clear();
		for (auto &tri : polygon->triangles)
		{
			tri.isPath = false;
		}

		clicknum += 1;
		int t = clicknum % 3;
		if (t == 0) // 第3次点击，清空起点终点
		{
			fromx = fromy = tox = toy = -1;
#ifdef WIN32
			system("cls");
#endif
		}
		else if (t == 1) // 第1次点击，获取起点
		{
			fromx = x;
			fromy = y;
		}
		else if (t == 2) // 第2次点击，获取终点，并寻路
		{
			tox = x;
			toy = y;
			ways = polygon->FindPath(Point(fromx, fromy), Point(tox, toy));
		}
	}
	// 我们每次点击窗口其实都会触发myDisplay()函数，如果是鼠标触发的，那么会先执行myClick()再执行myDisplay()
}

int main(int argc, char *argv[])
{
#ifdef WIN32
	// Windows平台下编译的，设置下控制台输出字符的编码
	SetConsoleOutputCP(CP_UTF8);
#endif
	
	// glut库初始化
	glutInit(&argc, argv);
	// 指定窗口使用RGB颜色模式（红、绿、蓝），单缓冲模式，绘制的内容会直接出现在屏幕上
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
	glutInitWindowPosition(30, 30); // 定义窗口位置
	glutInitWindowSize(1000, 800);	// 定义窗口大小
	glutCreateWindow("navmesh test"); // 创建窗口（标题）

	// gluOrtho2D(x_min, x_max, y_min, y_max)
	// 设置一个二维正交投影矩阵，将世界坐标系中的一个矩形区域映射到Viewport上。
	// 这个矩形区域由参数 (x_min, x_max) 和 (y_min, y_max) 定义
	// 图形编程中，y轴的方向通常是向下的
	gluOrtho2D(0, 1000, 800, 0);
	// 当窗口需要重新绘制时（例如窗口被激活或大小改变），myDisplay函数会被回调调用
	glutDisplayFunc(myDisplay);
	// 当鼠标事件（按下或释放）发生时，myClick函数会被回调调用
	glutMouseFunc(myClick);
	// 主循环，直到窗口被关闭，这个函数会不断检测事件（如键盘输入、鼠标事件、窗口大小改变等）并调用相应的回调函数。
	glutMainLoop();
	return 0;
}